package org.spring.aop.framework;

import lombok.AllArgsConstructor;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spring.aop.AdvisedSupport;
import org.spring.aop.TargetSource;
import org.tool.TipException;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * Spring AOP 框架的基于 CGLIB 的 {@link AopProxy} 实现。
 * <p>
 * 这种类型的对象应该通过代理工厂获得，由 {@link AdvisedSupport} 对象配置。这个类是 Spring 的 AOP 框架内部的，不需要由客户端代码直接使用。
 * <p>
 * {@link DefaultAopProxyFactory} 将在必要时自动创建基于 CGLIB 的代理，例如在代理目标类的情况下（请参阅 {@link DefaultAopProxyFactory}）。
 * <p>
 * 如果底层（目标）类是线程安全的，则使用此类创建的代理是线程安全的。
 *
 * @author 李坤
 * @date 2022/4/18 16:32
 */
@AllArgsConstructor
public class CglibAopProxy implements AopProxy {

    private static final Logger logger = LoggerFactory.getLogger(CglibAopProxy.class);

    /**
     * AOP 代理配置管理器
     */
    private final AdvisedSupport advised;

    @Override
    public Object getProxy() {
        return this.getProxy(null);
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        logger.debug("开始创建 CGLIB 动态代理");

        Class<?> targetClass;
        TargetSource targetSource = advised.getTargetSource();

        if (Objects.nonNull(targetSource) && Objects.nonNull(targetClass = targetSource.getTargetClass())) {
            Enhancer enhancer = new Enhancer();

            // 设置代理类的父类
            enhancer.setSuperclass(targetClass);
            // 设置类加载器
            enhancer.setClassLoader(classLoader);
            // 设置代理逻辑对象
            enhancer.setCallback(new DynamicAdvisedInterceptor(advised));

            // 创建代理类
            return enhancer.create();
        }
        throw new TipException("被代理类不能为空");
    }

    /**
     * 通用 AOP 回调。当目标是动态的或代理未冻结时使用。
     */
    @AllArgsConstructor
    private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

        private static final Logger logger = LoggerFactory.getLogger(CglibMethodInvocation.class);

        /**
         * AOP 代理配置管理器
         */
        private final AdvisedSupport advised;

        @Override
        public Object intercept(Object proxy, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable {
            Object target = advised.getTargetSource().getTarget();
            // 获取代理对象的方法拦截器（代理/增强方法）
            MethodInvocation invocation = new CglibMethodInvocation(target, method, arguments, proxy, methodProxy);

            // 判断是否是代理对象的方法
            if (advised.getMethodMatcher().matches(method, target.getClass())) {
                // 执行代理对象的方法拦截器（代理/增强方法）
                return advised.getMethodInterceptor().invoke(invocation);
            }
            // 执行被代理对象的方法
            return invocation.proceed();
        }

    }

    /**
     * 此 AOP 代理使用的 AOP Alliance MethodInvocation 的实现。
     */
    static class CglibMethodInvocation extends ReflectiveMethodInvocation {

        private static final Logger logger = LoggerFactory.getLogger(CglibMethodInvocation.class);

        /**
         * 代理/增强方法
         */
        private final MethodProxy methodProxy;

        /**
         * CglibMethodInvocation 构造函数
         *
         * @param target      被代理对象
         * @param method      被代理对象的方法
         * @param arguments   方法参数
         * @param methodProxy 代理/增强方法
         */
        public CglibMethodInvocation(Object target, Method method, Object[] arguments, Object proxy, MethodProxy methodProxy) {
            super(target, method, arguments, proxy);
            this.methodProxy = methodProxy;
        }

        @Override
        public Object proceed() throws Throwable {
            return methodProxy.invoke(target, arguments);
        }

    }

}
